using System;
using Core;
using Core.Extensions;


namespace SolarDevices
{
    public class OutbackRegisterTable : IRegisterTable
    {

        readonly Register[] _outbackFXRegister;
        readonly Register[] _outbackFMRegister;
        private SerialDevice _serialDevice;
        private byte[] _sample;
        protected char[] _data;

        public OutbackRegisterTable()
        {
            _sample = new byte[] {
                0x0a, 0x30, 0x2c, 0x31, 0x32, 0x2c, 0x36, 0x32, 0x2c, 0x30, 0x30, 0x2c, 0x30, 0x30, 0x31, 0x2c, 0x31, 0x31, 0x39, 0x2c, 0x30, 0x30, 0x2c, 0x30, 0x32, 0x2c, 0x30, 0x30, 0x38, 0x2c, 0x30, 0x30, 0x2c, 0x32, 0x34, 0x38, 0x2c, 0x30, 0x30, 0x38, 0x2c, 0x30, 0x30, 0x30, 0x2c, 0x30, 0x33, 0x37, 0x0d
            };
            _outbackFXRegister = new[]{
                                         new Register { Address = 4, Label = "Inverter current", UnitOfMeasure = "Amps", Conversion = address => TwoDigit(address)},
                                         new Register { Address = 7, Label = "Charger current", UnitOfMeasure = "Amps", Conversion = address => TwoDigit(address)},
                                         new Register { Address = 10, Label = "Buy current", UnitOfMeasure = "Amps", Conversion = address => TwoDigit(address)},
                                         new Register { Address = 13, Label = "AC input voltage", UnitOfMeasure = "Volts", Conversion = address => ThreeDigit(address)},
                                         new Register { Address = 17, Label = "AC output voltage", UnitOfMeasure = "Volts", Conversion = address => ThreeDigit(address)},
                                         new Register { Address = 21, Label = "Sell current", UnitOfMeasure = "Amps", Conversion = address => TwoDigit(address)},
                                         new Register { Address = 24, Label = "Operating mode", UnitOfMeasure = "", Conversion = address => TwoDigit(address)},
                                         new Register { Address = 27, Label = "Error mode", UnitOfMeasure = "", Conversion = address => InverterError(address)},
                                         new Register { Address = 31, Label = "AC mode", UnitOfMeasure = "", Conversion = address => TwoDigit(address)},
                                         new Register { Address = 34, Label = "Battery voltage", UnitOfMeasure = "Volts", Conversion = address => ThreeDigitOneDec(address)},
                                         new Register { Address = 38, Label = "Misc", UnitOfMeasure = "", Conversion = address => ThreeDigit(address)},
                                         new Register { Address = 42, Label = "Warning mode", UnitOfMeasure = "", Conversion = address => ThreeDigit(address)}
                                     };
            _outbackFMRegister = new[]{
                                        /* 0 */ new Register { Address = 7, Label = "Charger current", UnitOfMeasure = "Amps", Conversion = address => TwoDigit(address)}, 
                                        /* 1 */ new Register { Address = 10, Label = "PV current", UnitOfMeasure = "Amps", Conversion = address => TwoDigit(address)},
                                        /* 2 */ new Register { Address = 13, Label = "PV input voltage", UnitOfMeasure = "Volts", Conversion = address => ThreeDigit(address)},
                                        /* 3 */ new Register { Address = 17, Label = "Daily kWH", UnitOfMeasure = "kWH", Conversion = address => ThreeDigitOneDec(address)},
                                        /* 4 */ new Register { Address = 21, Label = "Sell current", UnitOfMeasure = "Amps", Conversion = address => TwoDigit(address)},
                                        /* 5 */ new Register { Address = 24, Label = "Aux mode", UnitOfMeasure = "", Conversion = address => TwoDigit(address)},
                                        /* 6 */ new Register { Address = 27, Label = "Error mode", UnitOfMeasure = "", Conversion = address => ControllerErrors(address)},
                                        /* 7 */ new Register { Address = 31, Label = "Charger mode", UnitOfMeasure = "", Conversion = address => ChargeState(address)},
                                        /* 8 */ new Register { Address = 34, Label = "Battery voltage", UnitOfMeasure = "Volts", Conversion = address => ThreeDigitOneDec(address)},
                                        /* 9 */ new Register { Address = 38, Label = "Daily AH", UnitOfMeasure = "AH", Conversion = address => FourDigit(address)}
                                     };
            _serialDevice = SerialDevice.GetSingleton();
        }

        protected char[] Data
        {
            get { return _data; }
        }

        public void Dispose()
        {
            _serialDevice.Dispose();
            _serialDevice = null;
        }

        public void StartPolling()
        {
            _serialDevice.StartPolling();
        }

        public ChargeState ReadChargeState()
        {
            if (!_serialDevice.Connected)
            {
                return new ChargeState
                {
                    Connected = false
                };
            }
            if (LoadData(SerialDevice.OutbackDevice.MX))
            {
                try
                {
                    var watts = double.Parse(_outbackFMRegister[0].Text) * double.Parse(_outbackFMRegister[8].Text);
                    var chargeState = new ChargeState
                    {
                        BatteryVolts = _outbackFMRegister[8].Text,
                        PvVolts = _outbackFMRegister[2].Text,
                        BatteryAmps = _outbackFMRegister[0].Text,
                        EnergyToday = _outbackFMRegister[3].Text,
                        Watts = watts.ToString(),
                        State = _outbackFMRegister[7].Text,
                        PvAmps = _outbackFMRegister[1].Text,
                        TotalKwH = "",
                        ControllerError = _outbackFMRegister[6].Text,
                        Connected = true
                    };
                    return chargeState;
                }
                catch (Exception ex)
                {
                    //DebugLogger.TryLog(ex);
                }
            }

            return new ChargeState
            {
                Connected = false
            };
        }


        //public ArrayList ReadRegisters()
        //{
        //    var registerList = new ArrayList();
        //    var fxList = _serialDevice.GetData(SerialDevice.OutbackDevice.FX);
        //    if (fxList.Count == 0)
        //    {
        //        var ca = new char[_sample.Length];
        //        for(int i = 0; i < _sample.Length; i++)
        //        {
        //            ca[i] = (char) _sample[i];
        //        }
        //        fxList.Add(ca);
        //    }
        //    if (fxList.Count > 0)
        //    {
        //        _data = (char[])fxList[0];

        //        foreach (var register in _outbackFXRegister)
        //        {
        //            string text;
        //            if (register.UnitOfMeasure.Length > 0)
        //            {
        //                var b = new StringBuilder(register.Text);
        //                b.Append(" (");
        //                b.Append(register.UnitOfMeasure);
        //                b.Append(")");
        //                text = b.ToString();
        //            }
        //            else
        //            {
        //                text = register.Text;
        //            }
        //            registerList.Add(new DisplayRegister { Address = register.Address, Label = register.Label, Text = text });
        //        }
        //        return registerList;
        //    }
        //    return null;
        //}

        public void AddInverterData(ref ChargeState chargeState)
        {
            try
            {
                if (LoadData(SerialDevice.OutbackDevice.FX))
                {
                    var amps = int.Parse(_outbackFXRegister[0].Text);
                    var  volts = int.Parse(_outbackFXRegister[4].Text);
                    chargeState.InverterPower = (volts * amps).ToString();
                    amps = int.Parse(_outbackFXRegister[1].Text);
                    volts = int.Parse(_outbackFXRegister[3].Text);
                    chargeState.GeneratorPower = (volts * amps).ToString();
                    chargeState.InverterError = _outbackFXRegister[7].Text;
                    chargeState.ShowInverter = true;
                }
            }
            catch (Exception)
            {
                chargeState.ShowInverter = false;
            }

        }

        //private void LoadSample()
        //{
        //    var ca = new char[_sample.Length];
        //    for (int i = 0; i < _sample.Length; i++)
        //    {
        //        ca[i] = (char)_sample[i];
        //    }
        //    fxList.Add(ca);
        //}

        protected string TwoDigit(int offset)
        {
            return new string(new[] { Convert.ToChar(Data[offset]), Convert.ToChar(Data[offset + 1]) });
        }

        protected string ThreeDigit(int offset)
        {
            return new string(new[] { Convert.ToChar(Data[offset]), Convert.ToChar(Data[offset + 1]), Convert.ToChar(Data[offset + 2]) });
        }

        protected string ThreeDigitOneDec(int offset)
        {
            return new string(new[] { Convert.ToChar(Data[offset]), Convert.ToChar(Data[offset + 1]), '.', Convert.ToChar(Data[offset + 2]) });
        }

        protected string FourDigit(int offset)
        {
            return new string(new[] { Convert.ToChar(Data[offset]), Convert.ToChar(Data[offset + 1]), Convert.ToChar(Data[offset + 2]), Convert.ToChar(Data[offset + 3]) });
        }

        private string InverterError(int offset)
        {
            var code = new char[3];
            code[0] = Convert.ToChar(Data[offset]);
            code[1] = Convert.ToChar(Data[offset + 1]);
            code[2] = Convert.ToChar(Data[offset + 2]);
            var s = new StringBuilder();
            s.Append(code, 0, 3);
            var res = Convert.ToByte(s.ToString());
            return OutbackLookupTables.LookupInverterError(res);
        }

        public void ReadChargeEvent(ref ChargeEvent ce)
        {
            if (_serialDevice.Connected == false)
            {
                throw new Exception("No connection");
            }
            if (LoadData(SerialDevice.OutbackDevice.MX))
            {
                ce.EnergyToday = ThreeDigitOneDec(16);
                var val = TwoDigit(30);
                var code = int.Parse(val);
                switch (code)
                {
                    case 0:
                        ce.StateCode = StateCode.Resting;
                        break;
                    case 1:
                        ce.StateCode = StateCode.Float;
                        break;
                    case 2:
                        ce.StateCode = StateCode.Bulk;
                        break;
                    case 3:
                        ce.StateCode = StateCode.Absorb;
                        break;
                    case 4:
                        ce.StateCode = StateCode.Equalize;
                        break;
                }
            }
        }

        public void ReadInverterEvent(ref ChargeEvent ce)
        {
            if (_serialDevice.Connected)
            {
                try
                {
                    if (LoadData(SerialDevice.OutbackDevice.FX))
                    {
                        var ac = int.Parse(ThreeDigit(12));
                        ce.Generator = ac > 100;
                    }
                }
                catch (Exception ex)
                {
                    DebugLogger.TryLog(ex);
                }
              
            }

        }

        private bool LoadData(SerialDevice.OutbackDevice device)
        {
            bool rval = false;
            var fxList = _serialDevice.GetData(device);
            if (fxList.Count > 0)
            {
                _data = (char[]) fxList[0];
                rval = true;
            }
            return rval;
        }

        private string ChargeState(int address)
        {
            return ClassicLookupTables.LookupBatteryStage(int.Parse(TwoDigit(address)));
        }


        private string ControllerErrors(int address)
        {
            return OutbackLookupTables.LookupControllerError(uint.Parse(ThreeDigit(address)));
        }
    }
}